package so;

import comp.racer.common.interfaces.ClienteIF;
import comp.racer.common.interfaces.ServidorIF;
import comp.racer.common.rn.Jogador;
import comp.racer.common.so.Localizador;
import iu.ClassificacaoTela;
import iu.ConexaoTela;
import iu.PartidaTela;
import iu.RankingTela;
import iu.SalaDeEsperaTela;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Vector;
import javax.swing.JOptionPane;
import rn.Texto;

/**
 *
 * @author italo
 */
public class Cliente extends UnicastRemoteObject implements ClienteIF {

    // atributos especificados na FCR
    private ServidorIF servidor;
    private String login;
    private Map<String, Jogador> loginJogadorMap;

    
    private ConexaoTela telaConexao;
    private SalaDeEsperaTela telaSalaDeEspera;
    private PartidaTela telaPartida;
    private ClassificacaoTela telaClassificacao;
    private RankingTela telaRanking;

    // atributos adicionais
    private Localizador localizador;
    private static Cliente clienteSingleton;
    private static final int TEMPO_DE_INICIO = 2;
    private int timer;

    // constructores
    public Cliente() throws RemoteException, SocketException {
        super();
        setLoginJogadorMap(new HashMap<String, Jogador>());
    }


    // metodos publicos especificados na FCR
    public void realizaConexao(Localizador servidorLocalizador) throws RemoteException, NotBoundException, MalformedURLException {
        System.out.println("\nTentativa de conexao ao servidor: " + servidorLocalizador);
        servidor = (ServidorIF) Naming.lookup(servidorLocalizador.toString());

        System.out.println("Conectado ao servidor.");


        servidor.realizaConexao(localizador);
        System.out.println("\nResposta do servidor a solicitacao de conexao: ");
    }

    public boolean validaEntradaSalaDeEspera(String clienteLogin) {
        try {
            setLogin(clienteLogin);
            Map<String, Jogador> loginJogadorReturnMap = servidor.validaEntradaSalaDeEspera(getLocalizador(), clienteLogin);

            if (loginJogadorReturnMap == null) {
                System.out.println("Entrada invalidada!");
                return false;

            } else {
                setLoginJogadorMap(loginJogadorReturnMap);
                System.out.println("Entrada validada. Sala de espera agora estah com " + getLoginJogadorMap().size() + " jogadores.");
                iniciaTelaSalaDeEspera(loginJogadorReturnMap);

                return true;
            }

        } catch (Exception ex) {
            exibeMensagemErro(ex.getMessage());
        }

        return false;
    }

    public void notificaMudancaEstado() {
        try {
            getServidor().notificaMudancaDeEstado(getLogin());
        } catch (Exception ex) {
            exibeMensagemErro(ex.getMessage());
        }
    }

    public void notificaProgressoJogador(int nCaracteres) {
        try {
            servidor.notificaProgressoJogador(getJogadorToServidor(), nCaracteres);
        } catch (Exception ex) {
            exibeMensagemErro(ex.getMessage());
        }
    }

    public void realizaDesconexao(int status) {
        try {
            getServidor().realizaDesconexao(getLocalizador());
            System.exit(status);
        } catch (Exception ex) {
            exibeMensagemErro(ex.getMessage());
        }
    }

    //TODO Não existe mais método atualizaJogadores(), agora é atualizaJogador()
    public void atualizaJogador(Jogador jogadorBase) {
        System.out.println("Jogador " + jogadorBase + " atualizado no cliente " + getLocalizador());

        if (getLoginJogadorMap().containsKey(jogadorBase.getLogin())) {
            Jogador jogadorCliente = getLoginJogadorMap().get(jogadorBase.getLogin());
            jogadorCliente.merge(jogadorBase);
            jogadorCliente.atualizaJogador();

        } else {
            getLoginJogadorMap().put(jogadorBase.getLogin(), jogadorBase);
            jogadorBase.atualizaJogador();
        }

    }

    public void iniciaTelaConexao() {

        setTelaConexao(new ConexaoTela());
     

    }

    public void iniciaTelaSalaDeEspera(Map<String, Jogador> loginJogadorReturnMap) {
        getTelaConexao().fechaTela();
        setTelaSalaDeEspera(new SalaDeEsperaTela());
        
    }

    public void iniciaTelaRanking() {
        try {
            List<Jogador> rankingList = obtemRanking();

            System.out.println("Ranking atual: \n");
            for (Jogador jogador : rankingList) {
                System.out.println(jogador);
            }
            setTelaRanking(new RankingTela(rankingList));
           

        } catch (RemoteException ex) {
            exibeMensagemErro(ex.getMessage());
        }
    }

    public void inicializaTelaPartida(String textoDaPartida) {
        try {
            System.out.println("Iniciando tela de partida em " + TEMPO_DE_INICIO + " segundos...");
            //TODO mudar o tempo
            Thread.sleep(TEMPO_DE_INICIO * 1000);

            

            List<String> listaString = new Vector<String>();
            String[] arrayStr = textoDaPartida.split("[,; ]+", -1);
            for (String string : arrayStr) {
                listaString.add(string);
            }

            Texto textoPartida = new Texto(listaString);


            getTelaSalaDeEspera().fechaTela();
            setTelaPartida(new PartidaTela(textoPartida));
            System.out.println("Tela de partida iniciada!");

        } catch (InterruptedException ex) {
            exibeMensagemErro(ex.getMessage());
        }
    }

    public void encerraTelaPartida(Map<Jogador, Integer> jogadorPosicaoRankingMap) {
        System.out.println("Tela de partida finalizada no cliente : " + getLocalizador());
        getTelaPartida().fechaTela();
        iniciaTelaClassificacao(jogadorPosicaoRankingMap);
    }

    public void iniciaTelaClassificacao(Map<Jogador, Integer> jogadorPosicaoRankingMap) {
        System.out.println("A classificacao da partida foi: ");

        setTelaClassificacao(new ClassificacaoTela(jogadorPosicaoRankingMap));
        getTelaClassificacao().setVisible(true);
    }

    public List<Jogador> obtemRanking() throws RemoteException {
        List<Jogador> rankingList = getServidor().obtemRanking();
        return rankingList;

    }

    public void exibeMensagemErro(String erroMsg) {
        JOptionPane.showMessageDialog(null, erroMsg, "Erro", JOptionPane.ERROR_MESSAGE);
    }

    // metodos pulicos adicionais
    public static Cliente getInstancia() {
        if (clienteSingleton == null) {
            try {
                clienteSingleton = new Cliente();
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(null, ex.getMessage(), "Erro", JOptionPane.ERROR_MESSAGE);
                System.exit(1);
            }
        }
        return clienteSingleton;
    }

    // metodos privados adicionais
    private void iniciaCliente() {
        try {
            ArquivoUtil arquivoUtil = new ArquivoUtil();
            setLocalizador(arquivoUtil.obterClienteLocalizador());


            Random radom = new Random();
            getLocalizador().setPort(new Integer(1100 + radom.nextInt(100)));


            setUpEnvironment();
            setUpService();

            Localizador servidorLocalizador = arquivoUtil.obterServidorLocalizador();

            getInstancia().realizaConexao(servidorLocalizador);
        } catch (Exception ex) {
            exibeMensagemErro(ex.getMessage());
            System.exit(1);
        }

        iniciaTelaConexao();

    }

    private static void setUpService() throws RemoteException, SocketException {
        Cliente cliente = getInstancia();

        System.out.println("3 - Registrando o servico.");

        Registry registry = LocateRegistry.createRegistry(cliente.getLocalizador().getPort());
        registry.rebind(cliente.getLocalizador().getServiceName(), cliente);

        System.out.println("4 - Cliente rodando em: " + cliente.getLocalizador());
    }

    private static void setUpEnvironment() {
        System.out.println("1 - Inicializando variaveis de ambiente.");

        String projectPath = System.getProperty("user.dir");

        System.setProperty("java.security.policy", "file:" + projectPath + "/cliente.policy");
        System.setProperty("java.rmi.server.codebase", "file:" + projectPath);

        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new RMISecurityManager());
        }

        System.out.println("2 - Variaveis de ambiente inicializadas.");
    }

    // override
    @Override
    public String toString() {
        return "Jogador: " + getJogador() + " no localizador: " + getLocalizador();
    }

    public static void main(String[] args) {
        getInstancia().iniciaCliente();

    }

    // getters e setters
    public Localizador getLocalizador() {
        return localizador;
    }

    public void setLocalizador(Localizador localizador) {
        this.localizador = localizador;
    }

    private ServidorIF getServidor() {
        return servidor;
    }

    private void setServidor(ServidorIF servidor) {
        this.servidor = servidor;
    }

    public Jogador getJogador() {
        return loginJogadorMap.get(getLogin());
    }

    public void setJogador(Jogador jogador) {
        setLogin(jogador.getLogin());
        getLoginJogadorMap().put(getLogin(), jogador);
    }

    public List<Jogador> getJogadores() {
        return new Vector<Jogador>(getLoginJogadorMap().values());
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public Map<String, Jogador> getLoginJogadorMap() {
        return loginJogadorMap;
    }

    public void setLoginJogadorMap(Map<String, Jogador> loginJogadorMap) {
        this.loginJogadorMap = loginJogadorMap;
    }

    public Jogador getJogadorToServidor() throws RemoteException {
        Jogador jogadorToServidor = (Jogador) getJogador();
        return jogadorToServidor;
    }

    private ConexaoTela getTelaConexao() {
        return telaConexao;
    }

    private void setTelaConexao(ConexaoTela telaConexao) {
        this.telaConexao = telaConexao;
    }

    private SalaDeEsperaTela getTelaSalaDeEspera() {
        return telaSalaDeEspera;
    }

    private void setTelaSalaDeEspera(SalaDeEsperaTela telaSalaDeEspera) {
        this.telaSalaDeEspera = telaSalaDeEspera;
    }

    private PartidaTela getTelaPartida() {
        return telaPartida;
    }

    private void setTelaPartida(PartidaTela telaPartida) {
        this.telaPartida = telaPartida;
    }

    private ClassificacaoTela getTelaClassificacao() {
        return telaClassificacao;
    }

    private void setTelaClassificacao(ClassificacaoTela telaClassificacao) {
        this.telaClassificacao = telaClassificacao;
    }

    private RankingTela getTelaRanking() {
        return telaRanking;
    }

    private void setTelaRanking(RankingTela telaRanking) {
        this.telaRanking = telaRanking;
    }
}
